/*
 *  Copyright (c) 2015-2018 Jose Osvaldo Suarez Domingos
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 * 
 */

///////////////////////////////////////////////////////////////////////////////

template<unsigned int Precision,
         typename Base_Type = uint64_t>
class big_uint
{
    //Esta clase es amiga de si misma en sus diferentes instanciaciones
    //para poder implementar el operador de "cast" sin problemas.
    template<unsigned int, typename> friend class big_uint;

  public:
    static const bool make_big_uint = true;
    static const unsigned int precision = Precision;
    using base_type = Base_Type;

  private:
    static const base_type zero = static_cast<base_type>(0);
    static const base_type one = static_cast<base_type>(1);
    static const base_type all = ~zero;

    static const unsigned int bits_per_block = sizeof(base_type) * 8;
    static const unsigned int blocks = precision % bits_per_block == 0 ?
                                       precision / bits_per_block :
                                       precision / bits_per_block + 1;
    static const unsigned int head_bits =
        blocks * bits_per_block - precision;
    static const base_type head_mask =
        head_bits == 0 ? all :
                         (one << (bits_per_block - head_bits)) - 1;

    //Contenido del numero.
    base_type data[blocks];

    ///////////////////////////////////////////////////////////////////////////

    //Si el tamano total no es multiplo de "base_type" el ultimo bloque
    //contiene bits sin usar. Hacemos que estos bits sean 0.
    void mask_head();
/*
    void divide_newton(const big_uint & divisor,
                       big_uint & quotient,
                       big_uint & remainder);
*/
    //Estas clases/funciones sirven para que el compilador no se queje
    //de que un tipo de datos distinto de "big_uint" no tiene el
    //atributo "data".
    struct get_data_helper
    {
        template<typename C>
        static const base_type * get_data(const C & c,
                                          decltype(C::make_big_uint) *)
        {
            return c.data;
        }

        template<typename C>
        static const base_type * get_data(const C &, ...)
        {
            return nullptr;
        }
    };

    template<typename T>
    static const base_type * get_data(const T & t)
    {
        return get_data_helper::get_data(t, nullptr);
    }

    template<unsigned int Base>
    static void from_string(const char * description,
                            const unsigned int length,
                            base_type * data,
                            const unsigned int blocks_data);

  public:
    static const bool is_dynamic = false;
    static const bool is_static = true;
    struct division_by_zero {};

    ///////////////////////////////////////////////////////////////////////////

    //Constructor por defecto.
    big_uint() {}

    ///////////////////////////////////////////////////////////////////////////

    //Constructor desde un tipo entero.
    template<typename Integer_Type,
             typename = typename enable_if
                 <!is_big_uint<Integer_Type>::value>::type>
    big_uint(Integer_Type value);

    //Constructor desde una cadena.
    //Son validas las bases 2, 8, 10 y 16.
    big_uint(const char * description, unsigned int base = 10);

    //Conversion.
    template<typename Integer_Type,
             typename = typename enable_if
                 <!is_big_uint<Integer_Type>::value>::type>
    explicit operator Integer_Type() const;

    //Conversion desde big_uint con el mismo tipo base.
    template<typename Integer_Type,
             typename = typename enable_if
                 <is_big_uint<Integer_Type>::value>::type,
             typename = typename enable_if
                 <is_same<base_type,
                  typename Integer_Type::base_type>::value>::type>
    operator Integer_Type() const;

    ///////////////////////////////////////////////////////////////////////////

    //Asignacion.
    template<typename T>
    big_uint & operator =(const T & n);

    ///////////////////////////////////////////////////////////////////////////

    //Suma con asignacion.
    template<typename T>
    big_uint & operator +=(const T & n);

    //Suma.
    template<typename T>
    big_uint operator +(const T & n) const;

    //Preincremento.
    big_uint & operator ++();

    //Postincremento.
    big_uint operator ++(int);

    //Opuesto
    big_uint operator -() const;

    //Resta con asignacion.
    template<typename T>
    big_uint & operator -=(const T & n);

    //Resta.
    template<typename T>
    big_uint operator -(const T & n) const;

    //Predecremento.
    big_uint & operator --();

    //Postdecremento.
    big_uint operator --(int);

    //Producto con asignacion.
    template<typename T>
    big_uint & operator *=(const T & n);

    //Producto.
    template<typename T>
    big_uint operator *(const T & n) const;

    //Producto con parte alta.
    template<typename T>
    static big_uint<Precision * 2, Base_Type>
    mul_high_low(const big_uint & a, const T & b);

    //Desplazamiento aritmetico a la izquierda.
    big_uint operator <<(unsigned int n) const;

    //Desplazamiento aritmetico a la izquierda con asignacion.
    big_uint & operator <<=(unsigned int n);

    //Desplazamiento aritmetico a la derecha.
    big_uint operator >>(unsigned int n) const;

    //Desplazamiento aritmetico a la derecha con asignacion.
    big_uint & operator >>=(unsigned int n);

    //Negacion logica.
    big_uint operator ~() const;

    //"O" logico con asignacion.
    template<typename T>
    big_uint & operator |=(const T & n);

    //"O" logico.
    template<typename T>
    big_uint operator |(const T & n) const;

    //"Y" logico con asignacion.
    template<typename T>
    big_uint & operator &=(const T & n);

    //"Y" logico.
    template<typename T>
    big_uint operator &(const T & n) const;

    //"O exclusivo" logico con asignacion.
    template<typename T>
    big_uint & operator ^=(const T & n);

    //"O exclusivo" logico.
    template<typename T>
    big_uint operator ^(const T & n) const;

    //Obtiene el valor del bit de la posicion "pos".
    unsigned int get_bit(unsigned int pos) const;

    //Asigna el valor "value" al bit de la posicion "pos".
    void set_bit(unsigned int pos,
                 unsigned int value);

    //Asigna el valor "value" a los bits del rango [start, end).
    void set_range(unsigned int start,
                   unsigned int end,
                   unsigned int value);

    //Comparaciones
    template<typename T>
    bool operator <(const T & n) const;
    template<typename T>
    bool operator ==(const T & n) const;
    template<typename T>
    bool operator <=(const T & n) const;
    template<typename T>
    bool operator >(const T & n) const;
    template<typename T>
    bool operator >=(const T & n) const;
    template<typename T>
    bool operator !=(const T & n) const;

    //Division.
    template<typename T>
    big_uint operator /(const T & n) const;

    //Division con asignacion.
    template<typename T>
    big_uint & operator /=(const T & n);

    //Modulo.
    template<typename T>
    big_uint operator %(const T & n) const;

    //Modulo con asignacion.
    template<typename T>
    big_uint & operator %=(const T & n);

    //Bit mas significativo.
    unsigned int msb() const;

    //Potencia.
    template<typename T>
    big_uint pow(const T & exp) const;

    //Potencia y modulo.
    template<typename T,
             typename U>
    big_uint pow_mod(const T & exp,
                     const U & mod) const;

    ///////////////////////////////////////////////////////////////////////////

    //Devuelve el tamano necesario en bytes para guardar la representacion
    //en texto del numero en las bases decimal, hexadecimal, octal y binaria,
    //sin incluir el terminador '\0'.
    unsigned int str_dec_bufsize() const {return precision / 3 + 1;}
    unsigned int str_hex_bufsize() const {return precision / 4 + 1;}
    unsigned int str_oct_bufsize() const {return precision / 3 + 1;}
    unsigned int str_bin_bufsize() const {return precision;}

    void snprint_dec(char * str, unsigned int size) const;
    void snprint_dec2(char * str, unsigned int size) const;
    void snprint_bin(char * str, unsigned int size) const;
    void snprint_hex(char * str, unsigned int size) const;
    void snprint_oct(char * str, unsigned int size) const;

  private:
    static void to_dec(char * str,
                       unsigned int size,
                       unsigned int & used_size,
                       const big_uint & number,
                       unsigned int digits,
                       bool at_left);

    ///////////////////////////////////////////////////////////////////////////
  public:
    void fprint_dec(FILE * stream) const;
    void fprint_bin(FILE * stream) const;
    void fprint_hex(FILE * stream) const;
    void fprint_oct(FILE * stream) const;

    void print_dec() const {fprint_dec(stdout);}
    void print_bin() const {fprint_bin(stdout);}
    void print_hex() const {fprint_hex(stdout);}
    void print_oct() const {fprint_oct(stdout);}
    void print() const {print_dec();}

    ///////////////////////////////////////////////////////////////////////////

#if !defined(NO_STL)
#define BIG_UINT_TO_STRING(base) \
    std::string str_##base() const\
    {\
        char buffer[str_##base##_bufsize() + 1];\
        snprint_##base(buffer, str_##base##_bufsize() + 1);\
        return buffer;\
    }
    BIG_UINT_TO_STRING(dec)
    BIG_UINT_TO_STRING(bin)
    BIG_UINT_TO_STRING(hex)
    BIG_UINT_TO_STRING(oct)
#undef BIG_UINT_TO_STRING

    std::string str() const {return str_dec();}
#endif

    ///////////////////////////////////////////////////////////////////////////

    void dump() const
    {
        //printf("blocks = %u\n", blocks);
        unsigned int j = 0;
        for (unsigned int i = blocks; i > 0; i--, j++)
        {
            printf("%.16" PRIX64, static_cast<uint64_t>(data[i - 1]));
            if ((j & 0x3) == 3)
                printf("\n");
            else
                printf(" ");
        }
        if ((j & 0x3) != 0)
            printf("\n");
        fflush(stdout);
    }
};

///////////////////////////////////////////////////////////////////////////////

#define TEMPLATE_BIG_UINT template<unsigned int Precision, \
                                   typename Base_Type>

#define BIG_UINT big_uint<Precision, Base_Type>

TEMPLATE_BIG_UINT
const unsigned int BIG_UINT::precision;

///////////////////////////////////////////////////////////////////////////////

//Si el tamano total no es multiplo de "base_type" el ultimo bloque
//contiene bits sin usar. Hacemos que estos bits sean 0.
TEMPLATE_BIG_UINT
inline
void BIG_UINT::mask_head()
{
    data[blocks - 1] &= head_mask;
}

///////////////////////////////////////////////////////////////////////////////
/*
TEMPLATE_BIG_UINT
inline
void BIG_UINT::divide_newton(const big_uint & divisor,
                             big_uint & quotient,
                             big_uint & remainder)
{
    using double_type = ...
    big_uint dividend = *this;
    big_uint divisor2 = divisor;

    //
    unsigned int msb = D2.data.msb();
    if (msb >= decimal_bits)
    {
        D2.data >>= msb - decimal_bits;
        N2.data >>= msb - decimal_bits;
    }
    else
    {
        D2.data <<= decimal_bits - msb;
        N2.data <<= decimal_bits - msb;
    }
    
}
*/
///////////////////////////////////////////////////////////////////////////////

//Constructor desde un tipo entero.
TEMPLATE_BIG_UINT
template<typename integer_type, typename>
inline
BIG_UINT::big_uint(integer_type value)
{
    if (is_negative<integer_type>(value))
    {
        unsigned int i = 0;
        while(value < ~static_cast<integer_type>(0) &&
              i < blocks)
        {
            data[i] = value;
            value = shift_right<bits_per_block>(value);
            i++;
        }

        fill_blocks<base_type>(data + i, blocks - i);
    }
    else
    {
        unsigned int i = 0;
        while(value > static_cast<integer_type>(0) &&
              i < blocks)
        {
            data[i] = value;
            value = shift_right<bits_per_block>(value);
            i++;
        }

        zero_blocks<base_type>(data + i, blocks - i);
    }

    mask_head();
}

///////////////////////////////////////////////////////////////////////////////

//Constructor desde una cadena.
//Son validas las bases 2, 8, 10 y 16.
TEMPLATE_BIG_UINT
inline
BIG_UINT::big_uint(const char * description,
                   const unsigned int base)
{
    bool error = false;

    if (!(base == 2 ||
          base == 8 ||
          base == 10 ||
          base == 16))
        error = true;

    //Averiguamos el tamano de la cadena y si la representacion es correcta.
    unsigned int i = 0;
    while(!error && description[i] != '\0')
    {
        const char c = description[i];

        if (base <= 10 &&
            (c < '0' || c > ('0' + static_cast<char>(base - 1))))
            error = true;
        else if ((c < '0' || c > '9') &&
                 (c < 'a' || c > 'f') &&
                 (c < 'A' || c > 'F'))
            error = true;

        i++;
    }

    zero_blocks<base_type>(data, blocks);

    if (error)
        return;

    const unsigned int length = i;

    switch(base)
    {
        case 2: from_string<2>(description, length, data, blocks); break;
        case 8: from_string<8>(description, length, data, blocks); break;
        case 10: from_string<10>(description, length, data, blocks); break;
        case 16: from_string<16>(description, length, data, blocks); break;
        default:;
    }

    mask_head();
}

template<unsigned int Base>
inline
unsigned int character_to_digit(const char c)
{
    return c - '0';    
}

template<>
inline
unsigned int character_to_digit<16>(const char c)
{
    if (c >= '0' && c <= '9')
        return c - '0';
    else if (c >= 'A' && c <= 'F')
        return c + 10 - 'A';
    else
        return c + 10 - 'a';
}

TEMPLATE_BIG_UINT
template<unsigned int Base>
inline
void BIG_UINT::from_string(const char * description,
                           const unsigned int length,
                           base_type * data,
                           const unsigned int blocks_data)
{
    const unsigned int base = Base;
    const unsigned int digits_per_block = bits_per_block /
                                          (base == 2 ?  1 :
                                           base == 8 ?  3 :
                                           base == 10 ? 3 :
                                                        4);
    if (length <= digits_per_block)
    {
        base_type b = zero;
        for (unsigned int i = 0; i < length; i++)
        {
            const char c = description[i];
            b = b * base + character_to_digit<base>(c);
        }
        data[0] += b;
    }
    else
    {
        const unsigned int blocks_tmp = (length / 2 + 1) * 4 /
                                        bits_per_block +
                                        1;
        base_type * tmp = new base_type[blocks_tmp];
        zero_blocks<base_type>(tmp, blocks_tmp);

        from_string<base>(description,
                          length / 2,
                          tmp,
                          blocks_tmp);

        big_uint p = big_uint(base).pow(length - length / 2);
        requete_fma<base_type>(data,
                               blocks_data,
                               tmp,
                               blocks_tmp,
                               p.data,
                               blocks);

        zero_blocks<base_type>(tmp, blocks_tmp);

        from_string<base>(description + length / 2,
                          length - length / 2,
                          tmp,
                          blocks_tmp);

        auto min = [](unsigned int a, unsigned int b)
        {return a < b ? a : b;};

        base_type carry = zero;
        addc_blocks<base_type>(data,
                               data,
                               tmp,
                               carry,
                               min(blocks_data, blocks_tmp));
        delete [] tmp;
    }
}

///////////////////////////////////////////////////////////////////////////////

//Conversion.
TEMPLATE_BIG_UINT
template<typename integer_type, typename>
inline
BIG_UINT::operator integer_type() const
{
    integer_type r = static_cast<integer_type>(data[blocks - 1]);
    for (unsigned int i = blocks; i > 0; i--)
    {
        r = shift_left<bits_per_block>(r);

        r |= static_cast<integer_type>(data[i - 1]);
    }
    return r;
}

///////////////////////////////////////////////////////////////////////////////

//Conversion desde big_uint con el mismo tipo base.
TEMPLATE_BIG_UINT
template<typename integer_type, typename, typename>
inline
BIG_UINT::operator integer_type() const
{
    integer_type out;
    for (unsigned int i = 0; i < blocks && i < out.blocks; i++)
        out.data[i] = data[i];

    if (out.blocks > blocks)
        zero_blocks<base_type>(out.data + blocks,
                               out.blocks - blocks);

    out.mask_head();
    return out;
}

///////////////////////////////////////////////////////////////////////////////

//Asignacion.
TEMPLATE_BIG_UINT
template<typename T>
inline
BIG_UINT & BIG_UINT::operator =(const T & n)
{
    if (is_same<T, big_uint>::value ||
        conversion_loses_precision<T, base_type>::value)
    {
        const base_type * operand;
        big_uint * casted;
        if (!is_same<T, big_uint>::value)
        {
            casted = new big_uint(n);
            operand = casted->data;
        }
        else
        {
            operand = get_data(n);
        }

        copy_blocks<base_type>(data,
                               operand,
                               blocks);

        if (!is_same<T, big_uint>::value)
            delete casted;
    }
    else
    {
        data[0] = static_cast<base_type>(n);
        if (is_negative<T>(n))
            fill_blocks<base_type>(data + 1, blocks - 1);
        else
            zero_blocks<base_type>(data + 1, blocks - 1);
    }
    
    mask_head();
    return *this;
}

///////////////////////////////////////////////////////////////////////////////

//Suma con asignacion.
TEMPLATE_BIG_UINT
template<typename T>
inline
BIG_UINT & BIG_UINT::operator +=(const T & n)
{
    base_type carry = zero;

    if (is_same<T, big_uint>::value ||
        conversion_loses_precision<T, base_type>::value)
    {
        const base_type * operand;
        big_uint * casted;
        if (!is_same<T, big_uint>::value)
        {
            casted = new big_uint(n);
            operand = casted->data;
        }
        else
        {
            operand = get_data(n);
        }
            
        addc_blocks<base_type>(data,
                               data,
                               operand,
                               carry,
                               blocks);

        if (!is_same<T, big_uint>::value)
            delete casted;
    }
    else
    {
        data[0] = addc<base_type>(data[0],
                                  static_cast<base_type>(n),
                                  carry);

        if (carry && !is_negative<T>(n))
            inc_blocks<base_type>(data + 1,
                                  carry,
                                  blocks - 1);
        else if (!carry && is_negative<T>(n))
            dec_blocks<base_type>(data + 1,
                                  carry,
                                  blocks - 1);
    }

    mask_head();
    return *this;
}

///////////////////////////////////////////////////////////////////////////////

//Suma.
TEMPLATE_BIG_UINT
template<typename T>
inline
BIG_UINT BIG_UINT::operator +(const T & n) const
{
    big_uint r; //Variable de retorno.

    base_type carry = zero;

    if (is_same<T, big_uint>::value ||
        conversion_loses_precision<T, base_type>::value)
    {
        const base_type * operand;
        big_uint * casted;
        if (!is_same<T, big_uint>::value)
        {
            casted = new big_uint(n);
            operand = casted->data;
        }
        else
        {
            operand = get_data(n);
        }
            
        addc_blocks<base_type>(r.data,
                               data,
                               operand,
                               carry,
                               blocks);

        if (!is_same<T, big_uint>::value)
            delete casted;
    }
    else
    {
        r.data[0] = addc<base_type>(data[0],
                                    static_cast<base_type>(n),
                                    carry);

        copy_blocks<base_type>(r.data + 1,
                               data + 1,
                               blocks - 1);
        if (carry && !is_negative<T>(n))
            inc_blocks<base_type>(r.data + 1,
                                  carry,
                                  blocks - 1);
        else if (!carry && is_negative<T>(n))
            dec_blocks<base_type>(r.data + 1,
                                  carry,
                                  blocks - 1);
    }

    r.mask_head();
    return r;
}

///////////////////////////////////////////////////////////////////////////////

//Preincremento.
TEMPLATE_BIG_UINT
inline
BIG_UINT & BIG_UINT::operator ++()
{
    base_type carry = 1;
    inc_blocks<base_type>(data, carry, blocks);
    mask_head();
    return *this;
}

//Postincremento.
TEMPLATE_BIG_UINT
inline
BIG_UINT BIG_UINT::operator ++(int)
{
    big_uint r;
    copy_blocks<base_type>(r.data, data, blocks);
    base_type carry = 1;
    inc_blocks<base_type>(data, carry, blocks);
    mask_head();
    return r;
}

///////////////////////////////////////////////////////////////////////////////

//Opuesto
TEMPLATE_BIG_UINT
inline
BIG_UINT BIG_UINT::operator -() const
{
    big_uint r;
    base_type carry = 1;
    neg_blocks<base_type>(r.data, data, carry, blocks);
    r.mask_head();
    return r;
}

///////////////////////////////////////////////////////////////////////////////

//Resta con asignacion.
TEMPLATE_BIG_UINT
template<typename T>
inline
BIG_UINT & BIG_UINT::operator -=(const T & n)
{
    base_type carry = one;

    if (is_same<T, big_uint>::value ||
        conversion_loses_precision<T, base_type>::value)
    {
        const base_type * operand;
        big_uint * casted;
        if (!is_same<T, big_uint>::value)
        {
            casted = new big_uint(n);
            operand = casted->data;
        }
        else
        {
            operand = get_data(n);
        }
            
        subc_blocks<base_type>(data,
                               data,
                               operand,
                               carry,
                               blocks);

        if (!is_same<T, big_uint>::value)
            delete casted;
    }
    else
    {
        data[0] = addc<base_type>(data[0],
                                  ~static_cast<base_type>(n),
                                  carry);

        if (carry && is_negative<T>(n))
            inc_blocks<base_type>(data + 1,
                                  carry,
                                  blocks - 1);
        else if (!carry && !is_negative<T>(n))
            dec_blocks<base_type>(data + 1,
                                  carry,
                                  blocks - 1);
    }

    mask_head();
    return *this;
}


//Resta.
TEMPLATE_BIG_UINT
template<typename T>
inline
BIG_UINT BIG_UINT::operator -(const T & n) const
{
    big_uint r; //Variable de retorno.

    base_type carry = one;

    if (is_same<T, big_uint>::value ||
        conversion_loses_precision<T, base_type>::value)
    {
        const base_type * operand;
        big_uint * casted;
        if (!is_same<T, big_uint>::value)
        {
            casted = new big_uint(n);
            operand = casted->data;
        }
        else
        {
            operand = get_data(n);
        }
            
        subc_blocks<base_type>(r.data,
                               data,
                               operand,
                               carry,
                               blocks);

        if (!is_same<T, big_uint>::value)
            delete casted;
    }
    else
    {
        r.data[0] = addc<base_type>(data[0],
                                    ~static_cast<base_type>(n),
                                    carry);

        copy_blocks<base_type>(r.data + 1,
                               data + 1,
                               blocks - 1);

        if (carry && is_negative<T>(n))
            inc_blocks<base_type>(r.data + 1,
                                  carry,
                                  blocks - 1);
        else if (!carry && !is_negative<T>(n))
            dec_blocks<base_type>(r.data + 1,
                                  carry,
                                  blocks - 1);
    }

    r.mask_head();
    return r;
}

///////////////////////////////////////////////////////////////////////////////

//Predecremento.
TEMPLATE_BIG_UINT
inline
BIG_UINT & BIG_UINT::operator --()
{
    base_type carry = 0;
    dec_blocks<base_type>(data, carry, blocks);
    mask_head();
    return *this;
}

//Postdecremento.
TEMPLATE_BIG_UINT
inline
BIG_UINT BIG_UINT::operator --(int)
{
    big_uint r;
    copy_blocks<base_type>(r.data, data, blocks);
    base_type carry = 0;
    dec_blocks<base_type>(data, carry, blocks);
    mask_head();
    return r;
}

///////////////////////////////////////////////////////////////////////////////

//Producto con asignacion.
TEMPLATE_BIG_UINT
template<typename T>
inline
BIG_UINT & BIG_UINT::operator *=(const T & n)
{
    big_uint tmp;

    if (is_same<T, big_uint>::value ||
        conversion_loses_precision<T, base_type>::value)
    {
        const base_type * operand;
        big_uint * casted;
        if (!is_same<T, big_uint>::value)
        {
            casted = new big_uint(n);
            operand = casted->data;
        }
        else
        {
            operand = get_data(n);
        }

        fixed_size_multiply<base_type,
                            blocks,
                            blocks,
                            blocks>(tmp.data, data, operand);

        if (!is_same<T, big_uint>::value)
            delete casted;
    }
    else
    {
        base_type casted_n = static_cast<base_type>(n);
        fixed_size_multiply<base_type,
                            1,
                            blocks,
                            blocks>(tmp.data, &casted_n, data);
    }

    copy_blocks<base_type>(data, tmp.data, blocks);

    mask_head();
    return *this;
}

//Producto.
TEMPLATE_BIG_UINT
template<typename T>
inline
BIG_UINT BIG_UINT::operator *(const T & n) const
{
    big_uint r;

    if (is_same<T, big_uint>::value ||
        conversion_loses_precision<T, base_type>::value)
    {
        const base_type * operand;
        big_uint * casted;
        if (!is_same<T, big_uint>::value)
        {
            casted = new big_uint(n);
            operand = casted->data;
        }
        else
        {
            operand = get_data(n);
        }

        fixed_size_multiply<base_type,
                            blocks,
                            blocks,
                            blocks>(r.data, data, operand);
        /*
        variable_size_multiply<base_type>(r.data,
                                          blocks,
                                          data, 
                                          blocks,
                                          operand,
                                          blocks);*/

        if (!is_same<T, big_uint>::value)
            delete casted;
    }
    else
    {
        base_type casted_n = static_cast<base_type>(n);
        fixed_size_multiply<base_type,
                            1,
                            blocks,
                            blocks>(r.data, &casted_n, data);
    }

    r.mask_head();
    return r;
}

///////////////////////////////////////////////////////////////////////////////

TEMPLATE_BIG_UINT
template<typename T>
inline
big_uint<Precision * 2, Base_Type>
BIG_UINT::mul_high_low(const big_uint & a, const T & b)
{
    using double_type = big_uint<Precision * 2, Base_Type>;
    double_type r;

    if (is_same<T, big_uint>::value ||
        conversion_loses_precision<T, base_type>::value)
    {
        const base_type * operand;
        big_uint * casted;
        if (!is_same<T, big_uint>::value)
        {
            casted = new big_uint(b);
            operand = casted->data;
        }
        else
        {
            operand = get_data(b);
        }

        fixed_size_multiply<base_type,
                            blocks,
                            blocks,
                            double_type::blocks>(r.data, a.data, operand);

        if (!is_same<T, big_uint>::value)
            delete casted;
    }
    else
    {
        base_type casted_n = static_cast<base_type>(b);
        fixed_size_multiply<base_type,
                            1,
                            blocks,
                            double_type::blocks>(r.data, &casted_n, a.data);
    }
    
    r.mask_head();
    return r;
}

///////////////////////////////////////////////////////////////////////////////

//Desplazamiento aritmetico a la izquierda.
TEMPLATE_BIG_UINT
inline
BIG_UINT BIG_UINT::operator <<(unsigned int n) const
{
    big_uint r;
    shift_left_big(r.data, data, n, blocks);
    r.mask_head();
    return r;
}

//Desplazamiento aritmetico a la izquierda con asignacion.
TEMPLATE_BIG_UINT
inline
BIG_UINT & BIG_UINT::operator <<=(unsigned int n)
{
    big_uint r = *this << n;
    *this = r;
    return *this;
}

//Desplazamiento aritmetico a la derecha.
TEMPLATE_BIG_UINT
inline
BIG_UINT BIG_UINT::operator >>(unsigned int n) const
{
    big_uint r;
    shift_right_big(r.data, data, n, blocks);
    r.mask_head();
    return r;
}

//Desplazamiento aritmetico a la derecha con asignacion.
TEMPLATE_BIG_UINT
inline
BIG_UINT & BIG_UINT::operator >>=(unsigned int n)
{
    big_uint r = *this >> n;
    *this = r;
    return *this;
}

///////////////////////////////////////////////////////////////////////////////

//Negacion logica.
TEMPLATE_BIG_UINT
inline
BIG_UINT BIG_UINT::operator ~() const
{
    big_uint r;
    not_blocks<base_type>(r.data, data, blocks);
    r.mask_head();
    return r;
}

///////////////////////////////////////////////////////////////////////////////

//"O" logico con asignacion.
TEMPLATE_BIG_UINT
template<typename T>
inline
BIG_UINT & BIG_UINT::operator |=(const T & n)
{
    if (is_same<T, big_uint>::value ||
        conversion_loses_precision<T, base_type>::value)
    {
        const base_type * operand;
        big_uint * casted;
        if (is_same<T, big_uint>::value)
            operand = get_data(n);
        else
        {
            casted = new big_uint(n);
            operand = casted->data;
        }
            
        or_blocks<base_type>(data,
                             data,
                             operand,
                             blocks);

        if (!is_same<T, big_uint>::value)
            delete casted;
    }
    else
    {
        data[0] |= static_cast<base_type>(n);
        if (is_negative<T>(n))
            fill_blocks<base_type>(data + 1,
                                   blocks - 1);
    }

    mask_head();
    return *this;
}

///////////////////////////////////////////////////////////////////////////////

//"O" logico.
TEMPLATE_BIG_UINT
template<typename T>
inline
BIG_UINT BIG_UINT::operator |(const T & n) const
{
    big_uint r;

    if (is_same<T, big_uint>::value ||
        conversion_loses_precision<T, base_type>::value)
    {
        const base_type * operand;
        big_uint * casted;
        if (is_same<T, big_uint>::value)
            operand = get_data(n);
        else
        {
            casted = new big_uint(n);
            operand = casted->data;
        }
            
        or_blocks<base_type>(r.data,
                             data,
                             operand,
                             blocks);

        if (!is_same<T, big_uint>::value)
            delete casted;
    }
    else
    {
        r.data[0] = data[0] | static_cast<base_type>(n);
        if (is_negative<T>(n))
            fill_blocks<base_type>(r.data + 1,
                                   blocks - 1);
        else
            copy_blocks<base_type>(r.data + 1,
                                   data + 1,
                                   blocks - 1);
    }

    r.mask_head();
    return r;
}

///////////////////////////////////////////////////////////////////////////////

//"Y" logico con asignacion.
TEMPLATE_BIG_UINT
template<typename T>
inline
BIG_UINT & BIG_UINT::operator &=(const T & n)
{
    if (is_same<T, big_uint>::value ||
        conversion_loses_precision<T, base_type>::value)
    {
        const base_type * operand;
        big_uint * casted;
        if (is_same<T, big_uint>::value)
            operand = get_data(n);
        else
        {
            casted = new big_uint(n);
            operand = casted->data;
        }
            
        and_blocks<base_type>(data,
                              data,
                              operand,
                              blocks);

        if (!is_same<T, big_uint>::value)
            delete casted;
    }
    else
    {
        data[0] &= static_cast<base_type>(n);
        if (!is_negative<T>(n))
            zero_blocks<base_type>(data + 1,
                                   blocks - 1);
    }

    //mask_head();
    return *this;
}

///////////////////////////////////////////////////////////////////////////////

//"O" logico.
TEMPLATE_BIG_UINT
template<typename T>
inline
BIG_UINT BIG_UINT::operator &(const T & n) const
{
    big_uint r;

    if (is_same<T, big_uint>::value ||
        conversion_loses_precision<T, base_type>::value)
    {
        const base_type * operand;
        big_uint * casted;
        if (is_same<T, big_uint>::value)
            operand = get_data(n);
        else
        {
            casted = new big_uint(n);
            operand = casted->data;
        }
            
        and_blocks<base_type>(r.data,
                              data,
                              operand,
                              blocks);

        if (!is_same<T, big_uint>::value)
            delete casted;
    }
    else
    {
        r.data[0] = data[0] & static_cast<base_type>(n);
        if (!is_negative<T>(n))
            zero_blocks<base_type>(r.data + 1,
                                   blocks - 1);
        else
            copy_blocks<base_type>(r.data + 1,
                                   data + 1,
                                   blocks - 1);
    }

    r.mask_head();
    return r;
}

///////////////////////////////////////////////////////////////////////////////

//"O" exclusivo logico con asignacion.
TEMPLATE_BIG_UINT
template<typename T>
inline
BIG_UINT & BIG_UINT::operator ^=(const T & n)
{
    if (is_same<T, big_uint>::value ||
        conversion_loses_precision<T, base_type>::value)
    {
        const base_type * operand;
        big_uint * casted;
        if (is_same<T, big_uint>::value)
            operand = get_data(n);
        else
        {
            casted = new big_uint(n);
            operand = casted->data;
        }
            
        xor_blocks<base_type>(data,
                              data,
                              operand,
                              blocks);

        if (!is_same<T, big_uint>::value)
            delete casted;
    }
    else
    {
        data[0] ^= static_cast<base_type>(n);
        if (is_negative<T>(n))
            not_blocks<base_type>(data + 1,
                                  data + 1,
                                  blocks - 1);
    }

    mask_head();
    return *this;
}

///////////////////////////////////////////////////////////////////////////////

//"O" exclusivo logico.
TEMPLATE_BIG_UINT
template<typename T>
inline
BIG_UINT BIG_UINT::operator ^(const T & n) const
{
    big_uint r;

    if (is_same<T, big_uint>::value ||
        conversion_loses_precision<T, base_type>::value)
    {
        const base_type * operand;
        big_uint * casted;
        if (is_same<T, big_uint>::value)
            operand = get_data(n);
        else
        {
            casted = new big_uint(n);
            operand = casted->data;
        }
            
        xor_blocks<base_type>(r.data,
                              data,
                              operand,
                              blocks);

        if (!is_same<T, big_uint>::value)
            delete casted;
    }
    else
    {
        r.data[0] = data[0] ^ static_cast<base_type>(n);
        if (is_negative<T>(n))
            not_blocks<base_type>(r.data + 1,
                       data + 1,
                       blocks - 1);
        else
            copy_blocks<base_type>(r.data + 1,
                                   data + 1,
                                   blocks - 1);
    }

    r.mask_head();
    return r;
}

///////////////////////////////////////////////////////////////////////////////

TEMPLATE_BIG_UINT
inline
unsigned int BIG_UINT::get_bit(unsigned int pos) const
{
    if (pos >= precision)
        return 0;

    const unsigned int block_number = pos / bits_per_block;
    const unsigned int bits_skip = pos % bits_per_block;
    return (data[block_number] >> bits_skip) & one;
}

///////////////////////////////////////////////////////////////////////////////

TEMPLATE_BIG_UINT
inline
void BIG_UINT::set_bit(unsigned int value,
                       unsigned int pos)
{
    if (pos >= precision)
        return;

    const unsigned int block_number = pos / bits_per_block;
    const unsigned int bits_skip = pos % bits_per_block;

    if (value == 0)
    {
        base_type mask = ~(one << bits_skip);
        data[block_number] &= mask;
    }
    else
    {
        base_type mask = one << bits_skip;
        data[block_number] |= mask;
    }
}

///////////////////////////////////////////////////////////////////////////////

TEMPLATE_BIG_UINT
void BIG_UINT::set_range(unsigned int value,
                         unsigned int start,
                         unsigned int end)
{
    if (start > end ||
        end > precision)
        return;

    unsigned int pending_bits = end - start;
    for (unsigned int i = 0, pos = 0;
         i < blocks;
         i++, pos += bits_per_block)
    {
        if (start >= pos &&
            start < pos + bits_per_block)
        {
            unsigned int size1 = bits_per_block - start % bits_per_block;
            unsigned int size2 = pending_bits;
            unsigned int min_size = size1 < size2 ? size1 : size2;
            base_type mask = (one << min_size) - one;
            mask <<= start % bits_per_block;
            if (value == 0)
                data[i] &= ~mask;
            else
                data[i] |= mask;

            pending_bits -= min_size;
        }
        else if (start < pos &&
                 end >= pos &&
                 end < pos + bits_per_block)
        {
            base_type mask = (one << pending_bits) - one;
            if (value == 0)
                data[i] &= ~mask;
            else
                data[i] |= mask;

            pending_bits = 0;
        }
        else if (start < pos &&
                 end >= pos + bits_per_block)
        {
            if (value == 0)
                data[i] = zero;
            else
                data[i] = all;

            pending_bits -= bits_per_block;
        }
    }
}

///////////////////////////////////////////////////////////////////////////////

//Comparaciones
TEMPLATE_BIG_UINT
template<typename T>
inline
bool BIG_UINT::operator <(const T & n) const
{
    bool result;

    if (is_same<T, big_uint>::value ||
        conversion_loses_precision<T, base_type>::value)
    {
        const base_type * operand;
        big_uint * casted;
        if (!is_same<T, big_uint>::value)
        {
            casted = new big_uint(n);
            operand = casted->data;
        }
        else
        {
            operand = get_data(n);
        }

        result = less<base_type>(data, operand, blocks);

        if (!is_same<T, big_uint>::value)
            delete casted;
    }
    else
    {
        if (msb() <= bits_per_block)
            result = data[0] < static_cast<base_type>(n);
        else
            result = false;
    }

    return result;
}

TEMPLATE_BIG_UINT
template<typename T>
inline
bool BIG_UINT::operator ==(const T & n) const
{
    bool result;

    if (is_same<T, big_uint>::value ||
        conversion_loses_precision<T, base_type>::value)
    {
        const base_type * operand;
        big_uint * casted;
        if (!is_same<T, big_uint>::value)
        {
            casted = new big_uint(n);
            operand = casted->data;
        }
        else
        {
            operand = get_data(n);
        }

        result = equal<base_type>(data, operand, blocks);

        if (!is_same<T, big_uint>::value)
            delete casted;
    }
    else
    {
        if (msb() <= bits_per_block)
            result = data[0] == static_cast<base_type>(n);
        else
            result = false;
    }

    return result;
}

//Menor o igual que.
TEMPLATE_BIG_UINT
template<typename T>
inline
bool BIG_UINT::operator <=(const T & n) const
{
    bool result;

    if (is_same<T, big_uint>::value ||
        conversion_loses_precision<T, base_type>::value)
    {
        const base_type * operand;
        big_uint * casted;
        if (!is_same<T, big_uint>::value)
        {
            casted = new big_uint(n);
            operand = casted->data;
        }
        else
        {
            operand = get_data(n);
        }

        result = less_equal<base_type>(data, operand, blocks);

        if (!is_same<T, big_uint>::value)
            delete casted;
    }
    else
    {
        if (msb() <= bits_per_block)
            result = data[0] <= static_cast<base_type>(n);
        else
            result = false;
    }

    return result;
}

//Mayor que.
TEMPLATE_BIG_UINT
template<typename T>
inline
bool BIG_UINT::operator >(const T & n) const
{
    return !(*this <= n);
}

//Mayor o igual que.
TEMPLATE_BIG_UINT
template<typename T>
inline
bool BIG_UINT::operator >=(const T & n) const
{
    return !(*this < n);
}

//Desigualdad.
TEMPLATE_BIG_UINT
template<typename T>
inline
bool BIG_UINT::operator !=(const T & n) const
{
    return !(*this == n);
}

///////////////////////////////////////////////////////////////////////////////

//Division.
TEMPLATE_BIG_UINT
template<typename T>
inline
BIG_UINT BIG_UINT::operator /(const T & n) const
{
    big_uint quotient, remainder;

    if (is_same<T, big_uint>::value ||
        conversion_loses_precision<T, base_type>::value)
    {
        const base_type * operand;
        big_uint * casted;
        if (!is_same<T, big_uint>::value)
        {
            casted = new big_uint(n);
            operand = casted->data;
        }
        else
        {
            operand = get_data(n);
        }

        divide(data, blocks,
               operand, blocks,
               quotient.data, blocks,
               remainder.data, blocks);

        if (!is_same<T, big_uint>::value)
            delete casted;
    }
    else
    {
        const base_type operand = static_cast<base_type>(n);
        divide(data, blocks,
               &operand, 1,
               quotient.data, blocks,
               remainder.data, blocks);
    }

    return quotient;
}

//Division con asignacion.
TEMPLATE_BIG_UINT
template<typename T>
inline
BIG_UINT & BIG_UINT::operator /=(const T & n)
{
    big_uint tmp;
    tmp = *this / n;
    *this = tmp;
    return *this;
}

//Modulo.
TEMPLATE_BIG_UINT
template<typename T>
inline
BIG_UINT BIG_UINT::operator %(const T & n) const
{
    big_uint quotient, remainder;

    if (is_same<T, big_uint>::value ||
        conversion_loses_precision<T, base_type>::value)
    {
        const base_type * operand;
        big_uint * casted;
        if (!is_same<T, big_uint>::value)
        {
            casted = new big_uint(n);
            operand = casted->data;
        }
        else
        {
            operand = get_data(n);
        }

        divide(data, blocks,
               operand, blocks,
               quotient.data, blocks,
               remainder.data, blocks);

        if (!is_same<T, big_uint>::value)
            delete casted;
    }
    else
    {
        const base_type operand = static_cast<base_type>(n);
        divide(data, blocks,
               &operand, 1,
               quotient.data, blocks,
               remainder.data, blocks);
    }

    return remainder;
}

//Modulo con asignacion.
TEMPLATE_BIG_UINT
template<typename T>
inline
BIG_UINT & BIG_UINT::operator %=(const T & n)
{
    big_uint tmp;
    tmp = *this % n;
    *this = tmp;
    return *this;
}

///////////////////////////////////////////////////////////////////////////////

//Bit mas significativo.
TEMPLATE_BIG_UINT
inline
unsigned int BIG_UINT::msb() const
{
    return msb_hint(data, blocks - 1);
}

///////////////////////////////////////////////////////////////////////////////

//Potencia.
TEMPLATE_BIG_UINT
template<typename T>
inline
BIG_UINT BIG_UINT::pow(const T & exp) const
{
    T e = exp;
    big_uint p = *this;
    big_uint r = 1;
    while(e != 0)
    {
        if (e & 0x1)
            r *= p;

        p *= p;
        e >>= 1;
    }

    return r;
}

///////////////////////////////////////////////////////////////////////////////

//Potencia con modulo.
TEMPLATE_BIG_UINT
template<typename T,
         typename U>
inline
BIG_UINT BIG_UINT::pow_mod(const T & exp,
                           const U & mod) const
{
    if (exp == 0) return 1;

    const big_uint m = *this % mod;
    if (exp == 1) return m;

    if (exp & 0x1)
        return (m * (m * m).pow_mod(exp >> 1, mod)) % mod;
    else
        return (m * m).pow_mod(exp >> 1, mod);
}

///////////////////////////////////////////////////////////////////////////

//Devuelve el tamano necesario en bytes para guardar la representacion
//en texto del numero en las bases decimal, hexadecimal, octal y binaria,
//sin incluir el terminador '\0'.

TEMPLATE_BIG_UINT
inline
void BIG_UINT::snprint_dec(char * str, unsigned int size) const
{
    //snprint_dec2(str, size);
    //return;

    unsigned int used_size;
    to_dec(str, size, used_size, *this, msb() * 10 / 32 + 1, true);

    //Si el resultado esta vacio se trata del 0. Lo escribimos.
    if (str[0] == '\0' && size >= 2)
    {
        str[0] = '0';
        str[1] = '\0';
    }
}

///////////////////////////////////////////////////////////////////////////////

TEMPLATE_BIG_UINT
inline
void BIG_UINT::snprint_bin(char * str, unsigned int size) const
{
    const unsigned int bufsize = str_bin_bufsize() + 1;
    char buffer[bufsize];
    signed int i = bufsize - 1;
    buffer[i--] = '\0';
    for (unsigned int j = 0; j < precision; j++)
        buffer[i--] = '0' + get_bit(j);

    if (static_cast<unsigned int>(i + 2) == bufsize)
        buffer[i--] = '0';

    strncpy(str, buffer + i + 1, size);
}

///////////////////////////////////////////////////////////////////////////////

TEMPLATE_BIG_UINT
inline
void BIG_UINT::snprint_hex(char * str, unsigned int size) const
{
    const unsigned int bufsize = str_hex_bufsize() + 1;
    char buffer[bufsize];
    signed int i = bufsize - 1;
    buffer[i--] = '\0';
    for (unsigned int j = 0; j < precision; j += 4)
    {
        unsigned int hex = 0;
        hex += get_bit(j + 0);
        hex += get_bit(j + 1) << 1;
        hex += get_bit(j + 2) << 2;
        hex += get_bit(j + 3) << 3;
        if (hex <= 9)
            buffer[i--] = '0' + hex;
        else
            buffer[i--] = 'A' + hex - 10;
    }

    if (static_cast<unsigned int>(i + 2) == bufsize)
        buffer[i--] = '0';

    strncpy(str, buffer + i + 1, size);
}

///////////////////////////////////////////////////////////////////////////////

TEMPLATE_BIG_UINT
inline
void BIG_UINT::snprint_oct(char * str, unsigned int size) const
{
    const unsigned int bufsize = str_oct_bufsize() + 1;
    char buffer[bufsize];
    signed int i = bufsize - 1;
    buffer[i--] = '\0';
    for (unsigned int j = 0; j < precision; j += 3)
    {
        unsigned int oct = 0;
        oct += get_bit(j + 0);
        oct += get_bit(j + 1) << 1;
        oct += get_bit(j + 2) << 2;
        buffer[i--] = '0' + oct;
    }

    if (static_cast<unsigned int>(i + 2) == bufsize)
        buffer[i--] = '0';

    strncpy(str, buffer + i + 1, size);
}

///////////////////////////////////////////////////////////////////////////////

#define BIG_UINT_FPRINT(base) \
    TEMPLATE_BIG_UINT\
    inline\
    void BIG_UINT::fprint_##base(FILE * stream) const\
    {\
        char buffer[str_##base##_bufsize() + 1];\
        snprint_##base(buffer, str_##base##_bufsize() + 1);\
        fprintf(stream, "%s", buffer);\
    }
    BIG_UINT_FPRINT(dec)
    BIG_UINT_FPRINT(bin)
    BIG_UINT_FPRINT(hex)
    BIG_UINT_FPRINT(oct)
#undef BIG_UINT_FPRINT

///////////////////////////////////////////////////////////////////////////////

TEMPLATE_BIG_UINT
void BIG_UINT::to_dec(char * str,
                      unsigned int size,
                      unsigned int & used_size,
                      const big_uint & number,
                      unsigned int digits,
                      bool at_left)
{
    //printf("numero = %s\n", number.str_hex().c_str());

    if (digits > sizeof(base_type) * 2)
    {
        //Partimos el numero en "p" partes con el mismo numero de
        //digitos decimales.
        //const unsigned int p = 2;
        const unsigned int p = number.data[blocks - 1] != zero ? 3 : 2;

        //Elegimos cuantos digitos tendra cada parte.
        unsigned int slice[p];
        for (unsigned int i = 0; i < p; i++)
            slice[i] = digits / p;
        unsigned int remainder = digits - slice[0] * p;
        for (unsigned int i = 0; i < p && remainder > 0; i++)
        {
            slice[i]++;
            remainder--;
        }

        //printf("digits = %u\n", digits);
        //for (unsigned int i = 0; i < p; i++)
        //printf("slice[%u] = %u\n", i, slice[i]);

        //Calculamos las potencias de 10 necesarias para partir
        //el numero.
        //big_uint pow1 = big_uint(10).pow(slice[1] + slice[2]);
        //big_uint pow2 = big_uint(10).pow(slice[2]);
        big_uint pow[p - 1];
        for (unsigned int i = 0; i < p - 1; i++)
        {
            unsigned int sum = 0;
            for (unsigned int j = i + 1; j < p; j++)
                sum += slice[j];

            //pow[i] = big_uint(5).pow(sum) << sum;
            pow[i] = big_uint(10).pow(sum);
            //printf("sum = %u\n", sum);
            //printf("msb = %u\n", pow[i].msb());
            //printf("pow[%u] = %s\n", i, pow[i].str_hex().c_str());
            //printf("precision = %u\n", precision);
        }

        //Partimos el numero.
        big_uint part[p], rem, rem2;
        for (unsigned int i = 0; i < p; i++)
            part[i] = static_cast<big_uint>(0);
        switch(p)
        {
            case 2:
                gueb::divide(number.data, blocks,
                             pow[0].data, blocks,
                             part[0].data, blocks,
                             part[1].data, blocks);
                break;
            case 3:
                gueb::divide(number.data, blocks,
                             pow[0].data, blocks,
                             part[0].data, blocks,
                             rem.data, blocks);
                gueb::divide(rem.data, blocks,
                             pow[1].data, blocks,
                             part[1].data, blocks,
                             part[2].data, blocks);
                break;
            case 4:
                gueb::divide(number.data, blocks,
                             pow[0].data, blocks,
                             part[0].data, blocks,
                             rem.data, blocks);
                gueb::divide(rem.data, blocks,
                             pow[1].data, blocks,
                             part[1].data, blocks,
                             rem2.data, blocks);
                gueb::divide(rem2.data, blocks,
                             pow[2].data, blocks,
                             part[2].data, blocks,
                             part[3].data, blocks);
                break;
            default:
                break;
        }

        using buint2 = big_uint<precision / 2 + 1, base_type>;
        //using buint2 = big_uint;

        //Llamamos recursivamente a esta funcion para seguir
        //partiendo cada parte.
        used_size = 0;
        for (unsigned int i = 0; i < p; i++)
        {
            unsigned int s;
            buint2::to_dec(str + used_size,
                           size - used_size,
                           s,
                           static_cast<buint2>(part[i]),
                           slice[i],
                           at_left && used_size == 0);
            used_size += s;
        }
    }
    else
    {
        //Si la parte es suficientemente pequena la convertimos
        //a decimal directamente.

        base_type n = number.data[0];
        unsigned int i;
        for (i = 0;
             i < digits &&
             i < size - 1;
             i++)
        {
            base_type q = n / 10;
            base_type r = n - q * 10;
            char c = '0' + static_cast<uint8_t>(r);
            str[digits - i - 1] = c;
            n = q;
        }
        str[i] = '\0';
        used_size = i;
        //printf("parcial: %s\n", str);

        //Si estamos en la izquierda del numero y empieza por
        //uno o mas ceros los eliminamos.
        if (at_left)
        {
            unsigned int j;
            for (j = 0; j < i; j++)
                if (str[j] != '0')
                    break;

            for (unsigned int k = 0; k < i - j; k++)
                str[k] = str[k + j];

            str[i - j] = '\0';
            used_size = i - j;
        }
    }
}

///////////////////////////////////////////////////////////////////////////////
